/**
 * Copyright 2015 jianglibo@gmail.com
 *
 */
package com.m3958.encode.detector;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.m3958.encode.detector.impl.Gb2312;
import com.m3958.encode.detector.impl.Gbk;
import com.m3958.encode.detector.impl.UTF8;

/**
 * @author jianglibo@gmail.com
 *         2015年12月10日
 *
 */
public class DetactorClazz {

    private static final List<Class<? extends AbstractDetector>> allClasses = new ArrayList<Class<? extends AbstractDetector>>();

    static {
        allClasses.add(UTF8.class);
        allClasses.add(Gb2312.class);
        allClasses.add(Gbk.class);
    }

    private static final LinkedHashMap<LanguageName, Map<String, Class<? extends AbstractDetector>>> allClassesLanguageNameMap = new LinkedHashMap<LanguageName, Map<String, Class<? extends AbstractDetector>>>();

    private static final LinkedHashMap<String, Class<? extends AbstractDetector>> allClassesEncodeMap = new LinkedHashMap<String, Class<? extends AbstractDetector>>();

    static {
        for (Class<? extends AbstractDetector> clazz : allClasses) {
            AbstractDetector ad;
            try {
                ad = clazz.newInstance();
                LanguageName ln = ad.getLanguageName();
                String encode = ad.getCharsetName();

                allClassesEncodeMap.put(encode, clazz);

                if (!allClassesLanguageNameMap.containsKey(ln)) {
                    allClassesLanguageNameMap.put(ln, new LinkedHashMap<String, Class<? extends AbstractDetector>>());
                }
                allClassesLanguageNameMap.get(ln).put(encode, clazz);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    public static Iterator<Class<? extends AbstractDetector>> detectorIterator(LanguageName prefer) {
        return new DetectorIterator(prefer);
    }

    public static class DetectorIterator implements Iterator<Class<? extends AbstractDetector>> {

        private Map<String, Class<? extends AbstractDetector>> preferMap = new HashMap<String, Class<? extends AbstractDetector>>();

        private Iterator<Class<? extends AbstractDetector>> preferIt;

        private boolean preferDone = false;

        Iterator<Entry<String, Class<? extends AbstractDetector>>> allIt;

        private Class<? extends AbstractDetector> nxt = null;

        public DetectorIterator() {
            this.preferDone = true;
            this.allIt = allClassesEncodeMap.entrySet().iterator();
        }

        public DetectorIterator(LanguageName prefer) {
            if (prefer == null) {
                this.preferDone = true;
            } else {
                this.preferMap = allClassesLanguageNameMap.get(prefer);
                this.preferIt = preferMap.values().iterator();
            }
            this.allIt = allClassesEncodeMap.entrySet().iterator();
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.util.Iterator#hasNext()
         */
        @Override
        public boolean hasNext() {
            if (preferDone) {
                return hasNextInteral();
            } else {
                boolean preferHasNext = preferIt.hasNext();
                if (preferHasNext) {
                    nxt = preferIt.next();
                    return true;
                } else {
                    preferDone = true;
                    return hasNextInteral();
                }
            }
        }

        /**
         * @return
         */
        private boolean hasNextInteral() {
            boolean hasnxt = allIt.hasNext();
            if (!hasnxt) {
                return false;
            }
            Entry<String, Class<? extends AbstractDetector>> entry;

            while (allIt.hasNext()) {
                entry = allIt.next();
                if (preferMap.containsKey(entry.getKey())) { // 已经用过了。
                    continue;
                } else {
                    nxt = entry.getValue();
                    return true;
                }
            }
            return false;
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.util.Iterator#next()
         */
        @Override
        public Class<? extends AbstractDetector> next() {
            return nxt;
        }
    }
}
